﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using System.Net;
using System.IO;
using System.Threading;
using System.Net.Cache;
using System.Reflection;
using System.Windows.Forms;
using System.Runtime.Serialization.Formatters.Binary;

namespace MulticastTV
{

    /// <summary>
    /// This class is used to download particular web site with epg detailed informations like tv show genre, summary, actors, director...
    /// Parsing of that web site is done in webepg plugins.
    /// </summary>
    public class EpgDetails
    {
        public string AssemblyVersion
        {
            get
            {
                return Assembly.GetExecutingAssembly().GetName().Version.ToString();
            }
        }

        public static string CachePath = Globals.UserAppPath + @"\..\cache\details\";
        public delegate void delegateParentThreadEpgResult(epgDeatailsResult res);
        epgDeatailsResult result;
        EPGWindowDaily gui;
        const int DefaultTimeout = 9000; //  9s timeout
        HttpRequestCachePolicy policy;
        Types.AvailablePlugin selectedPlugin;
        ASCIIEncoding encoding = new ASCIIEncoding();
        ManualResetEvent mre = new ManualResetEvent(true);
        bool VersionOk = false;

        /// <summary>
        /// Constructor
        /// </summary>
        public EpgDetails(epgDeatailsResult res, EPGWindowDaily form)
        {   
            result = res;
            gui = form;
            selectedPlugin = GlobalP.Plugins.AvailablePlugins.Find(Properties.Settings.Default.EPGPlugin);
            if (selectedPlugin != null && AssemblyVersion.CompareTo(selectedPlugin.Instance.MulticasttvVersion) >= 0)
            {
                VersionOk = true;
            }
            policy = new HttpRequestCachePolicy(HttpRequestCacheLevel.Default);
            HttpWebRequest.DefaultCachePolicy = policy;
        }

        /// <summary>
        /// Start downloading in new thread
        /// </summary>
        /// <param name="href">Link to page with details</param>
        public void start(string href)
        {
            ThreadStart starter = delegate { Get(href); };
            Thread th = new Thread(starter);
            th.IsBackground = true;
            th.Start();
        }
        
        /// <summary>
        /// Download epg details
        /// </summary>
        /// <param name="href">Link to page with details</param>
        private void Get(string href)
        {
            bool recvsign = mre.WaitOne(DefaultTimeout + 1000, true);
            if (!recvsign)
                return;
           
            mre.Reset();

            if (!VersionOk)
                return;
           
            if (readFromCache(href))
            {
                gui.Invoke(new delegateParentThreadEpgResult(gui.updateDetails), result);
                mre.Set();
                return;
            }

            bool xmltvparser = false;
            if (selectedPlugin.Instance.PluginType == "offline" && selectedPlugin.Instance.Name == "xmltv")
                xmltvparser = true;
            if (!xmltvparser)
            {
                getFromInternet(href);
            }
            else
            {
                if (XmltvParser.xmltvparser.Start())
                {
                    result.EpgData = XmltvParser.xmltvparser.ParseDetails(href);
                    //cacheData(href); not needed ??
                    gui.Invoke(new delegateParentThreadEpgResult(gui.updateDetails), result);
                }
            }
            mre.Set();
         }

        private void getFromInternet(string href)
        { 
            System.Uri uri = new Uri(href);
            HttpWebRequest wreq = (HttpWebRequest)WebRequest.Create(uri);

            wreq.Timeout = DefaultTimeout;
            if (Properties.Settings.Default.UserAgent == "")
                wreq.UserAgent = "MulticastTV/" + AssemblyVersion + " (compatible; MSIE 7.0; Windows NT 5.1)";
            else
                wreq.UserAgent = Properties.Settings.Default.UserAgent;
            wreq.Referer = selectedPlugin.Instance.HostName;
            wreq.Method = selectedPlugin.Instance.RequestMethod;
            wreq.ContentType = "application/x-www-form-urlencoded";

            string PostString;
            // TODO: This should be changed 
            if (href.Contains("?"))
                PostString = href.Split("?".ToCharArray(),2)[1];
            else
                PostString = href;

            if (wreq.Method == "POST")
            {
                byte[] byte1 = encoding.GetBytes(PostString);
                wreq.ContentLength = byte1.Length;
                Stream newStream = wreq.GetRequestStream();
                newStream.Write(byte1, 0, byte1.Length);
                newStream.Close();
            }

             if (downloadEpg(wreq, PostString, href))
            {
                cacheData(href);
                gui.Invoke(new delegateParentThreadEpgResult(gui.updateDetails), result);
            }
        }

        private bool readFromCache(string href)
        {
            string cachedFile = CachePath + Epg.validateFileName(href + ".dat");
            if (File.Exists(cachedFile))
            {
                FileStream fs = null;
                try
                {
                    fs = new FileStream(cachedFile, FileMode.Open);
                    BinaryFormatter formatter = new BinaryFormatter();
                    result.EpgData = (string[])formatter.Deserialize(fs);
                    fs.Close();
                    return true;
                }
                catch
                {
                    if (fs != null)
                        fs.Close();
                }
            }
            return false;        
        }

        private void cacheData(string href)
        {
            // Cache received data
            if (!Directory.Exists(CachePath))
                Directory.CreateDirectory(CachePath);

            FileStream fs = new FileStream(CachePath + Epg.validateFileName(href + ".dat"), FileMode.Create);
            try
            {
                //save to disk
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(fs, result.EpgData);
                fs.Close();
            }
            catch
            {
                fs.Close();
            }
        }

        private bool downloadEpg(HttpWebRequest req,
                                 string PostString,
                                 string href
                                 )
        {
            HttpWebResponse resp = null;
            try
            {
                resp = (HttpWebResponse)req.GetResponse();
            }
            catch
            {
                req = null;
                return false;
            }
            Stream RespStream = null;
            try
            {
                RespStream = resp.GetResponseStream();
            }
            catch
            {
                if (resp != null)
                    resp.Close();
                return false;
            }

            StreamReader sr = new StreamReader(RespStream);
            string epgPage = null;
            try
            {
                epgPage = sr.ReadToEnd();
            }
            catch
            {
            }

            // close everything
            sr.Dispose();
            RespStream.Close();
            if (resp != null)
                resp.Close();

            if (epgPage != null)
            {
                if (selectedPlugin != null)
                {
                    result.EpgData = selectedPlugin.Instance.ParseDetails(epgPage);
                    return true;
                }
            }
            return false;
        }
    }

    public class epgDeatailsResult
    {
        private string[] epg;

        public string[] EpgData
        {
            get { return epg; }
            set { epg = value; }
        }
    }
}
